//
// Created by thomas on 2020/5/21.
//
#include <arpa/inet.h>
#include <string.h>

#include "zm32_ap.h"
#include "uartlink.h"
#include "debug.h"
#include "utils.h"
#include "zm32_statemachine.h"


#define MAX_UNICAST_SZ          (255)
#define MAX_BROADCAST_SZ        (74)
#define MAX_MULTCAST_SZ         (73)

extern condition_val_ts read_conf, write_conf, nwking;
extern condition_val_ts nwking_state, tx_cnf, rx_cnf, join;

int zm32_getMessageLength( unsigned int u32Type, unsigned int *length )
{
    switch ( u32Type )
    {
        case E_SL_TMP_LOC_CONF_SET:
            *length = sizeof(struct tmploc_conf_tx_opt_resp);
            break;

        case E_SL_TMP_LOC_CONF_GROUP_ID:
            *length = sizeof(struct tmploc_conf_groupid_resp);
            break;

        case E_SL_TMP_LOC_FACTORY_MODE:
            *length = sizeof(struct tmploc_conf_groupid_resp);
            break;

        case E_SL_TMP_LOC_SET_NWK_ADDR:
            *length = sizeof(struct tmploc_set_nwkaddr_resp);
            break;

        case E_SL_PST_LOC_CONF_READ:
            *length = sizeof(struct pstloc_conf_read_resp);
            break;

        case E_SL_PST_LOC_AD_HOC:
            *length = sizeof(struct pstloc_join_mode_resp);
            break;

        case E_SL_PST_NWK_SET_REMOTE_CONF:
            *length = sizeof(struct pstnwk_set_remote_conf_resp);
            break;

        case E_SL_PST_LOC_COOR_STATE:
            *length = sizeof(struct pstloc_get_module_status_resp);
            break;

        case E_SL_SPC_RX_FRAME:
            *length = 14;
            break;

        case E_SL_SPC_GROUP_RX_FRAME:
            *length = 16;
            break;

        case E_SL_TMP_LOC_TX_MODE:
            *length = sizeof(struct tmploc_set_tx_mode_resp);
            break;

        case E_SL_TMP_MAC_RX_MODE:
            *length = sizeof(struct tmpmac_set_rx_mode_resp);
            break;

        case E_SL_PST_LOC_PERMIT_JOIN:
            *length = sizeof(struct pstloc_permit_join_resp);
            break;

        default:
            LOGE("Fatal error, u32Type[%04X] not set length", u32Type);
            *length = 0;
            return -1;
            break;
    }

    return 0;
}

int zm32_set_nwkaddr_req( unsigned short nwkaddr )
{
    teSL_Status status;
    struct tmploc_set_nwkaddr_req req;
    req.nwkaddr = nwkaddr;

    status = eSL_SendMessage(E_SL_TMP_LOC_SET_NWK_ADDR, sizeof(struct tmploc_set_nwkaddr_req), &req);
    if ( E_SL_OK != status )
    {
        return -1;
    }

    return 0;
}

void zm32_set_nwkaddr_resp( void *respmsg )
{
    struct tmploc_set_nwkaddr_resp *resp = ( struct tmploc_set_nwkaddr_resp * ) (respmsg);
    printf("resp->result:%d\n", resp->result);
}

/**
 * @brief 读取本地配置请求
 * @return
 */
int zm32_get_local_config_req( void )
{
    teSL_Status status;

    status = eSL_SendMessage(E_SL_PST_LOC_CONF_READ, 0, NULL);
    if ( E_SL_OK != status )
    {
        return -1;
    }

    return 0;
}

/**
 * @brief 读取本地配置回复
 * @param respmsg
 */
void zm32_get_local_config_resp( void *respmsg )
{
    struct pstloc_conf_read_resp *dup;
    struct pstloc_conf_read_resp *resp = ( struct pstloc_conf_read_resp * ) respmsg;

    resp->prototype = ntohs(resp->prototype);
    resp->firmwarever = ntohs(resp->firmwarever);
    resp->config.dstgroupid = ntohs(resp->config.dstgroupid);
    resp->config.dstnwkaddr = ntohs(resp->config.dstnwkaddr);
    resp->config.nwkaddr = ntohs(resp->config.nwkaddr);
    resp->config.panid = ntohs(resp->config.panid);
    resp->config.macaddr = ntohll(resp->config.macaddr);
    resp->config.dstmacaddr = ntohll(resp->config.dstmacaddr);

    if ( EN_UTILS_OK == utils_mem_dup(( void ** ) &dup, resp, sizeof(struct pstloc_conf_read_resp)) )
    {
        utils_cond_signal(&read_conf, dup);
    }
}

/**
 * @brief 配置组网方式请求
 * @param role 角色
 * @param opt 操作方式
 * @param mode 组网模式
 * @return
 */
int zm32_networking_node_req( dev_role_te role, operation_type_te opt, networking_mode_te mode )
{
    teSL_Status status;
    struct pstloc_join_mode_req req;

    req.rw = opt;
    req.mode = mode;
    req.role = role;

    status = eSL_SendMessage(E_SL_PST_LOC_AD_HOC, sizeof(struct pstloc_join_mode_req), &req);
    if ( E_SL_OK != status )
    {
        return -1;
    }

    return 0;
}

/**
 * @brief 配置组网方式回复
 * @return
 */
void zm32_networking_node_resp( void *respmsg )
{
    struct pstloc_join_mode_resp *dup;
    struct pstloc_join_mode_resp *resp = ( struct pstloc_join_mode_resp * ) respmsg;

    if ( EN_UTILS_OK == utils_mem_dup(( void ** ) &dup, resp, sizeof(struct pstloc_join_mode_resp)) )
    {
        utils_cond_signal(&nwking, dup);
    }
}

int zm32_reset_remote_req( unsigned short nwkaddr, unsigned short protocol )
{
    teSL_Status status;
    struct pstnwk_reset_remote_req req;

    req.nwkaddr = htons(nwkaddr);
    req.protocol = htons(protocol);

    status = eSL_SendMessage(E_SL_PST_NWK_RST_REMOTE, sizeof(struct pstnwk_reset_remote_req), &req);
    if ( E_SL_OK != status )
    {
        return -1;
    }

    return 0;
}

/**
 * @brief 设置远端设备配置，如果网络地址和设备自身网络地址匹配则设置自身
 * @param req
 * @return
 */
int zm32_set_remote_config_req( struct pstnwk_set_remote_conf_req *req )
{
    teSL_Status status;

    req->nwkaddr = htons(req->nwkaddr);
    req->config.dstgroupid = htons(req->config.dstgroupid);
    req->config.dstnwkaddr = htons(req->config.dstnwkaddr);
    req->config.nwkaddr = htons(req->config.nwkaddr);
    req->config.panid = htons(req->config.panid);
    req->config.macaddr = htonll(req->config.macaddr);
    req->config.dstmacaddr = htonll(req->config.dstmacaddr);

    status = eSL_SendMessage(E_SL_PST_NWK_SET_REMOTE_CONF, sizeof(struct pstnwk_set_remote_conf_req), req);
    if ( E_SL_OK != status )
    {
        return -1;
    }

    return 0;
}

/**
 * @brief 设置远端设备配置回复
 * @param respmsg
 */
void zm32_set_remote_config_resp( void *respmsg )
{
    struct pstnwk_set_remote_conf_resp *dup;
    struct pstnwk_set_remote_conf_resp *resp = ( struct pstnwk_set_remote_conf_resp * ) respmsg;

    resp->nwkaddr = ntohs(resp->nwkaddr);

    if ( EN_UTILS_OK == utils_mem_dup(( void ** ) &dup, resp, sizeof(struct pstnwk_set_remote_conf_resp)) )
    {
        utils_cond_signal(&write_conf, dup);
    }
}

/**
 * @brief 获取设备当前组网状态请求
 * @return
 */
int zm32_get_networking_state_req( void )
{
    teSL_Status status;

    status = eSL_SendMessage(E_SL_PST_LOC_COOR_STATE, 0, NULL);
    if ( E_SL_OK != status )
    {
        return -1;
    }

    return 0;
}

/**
 * @brief 获取设备当前组网状态回复
 * @param respmsg
 */
void zm32_get_networking_state_resp( void *respmsg )
{
    struct pstloc_get_module_status_resp *dup;
    struct pstloc_get_module_status_resp *resp = ( struct pstloc_get_module_status_resp * ) respmsg;

    if ( EN_UTILS_OK == utils_mem_dup(( void ** ) &dup, resp, sizeof(struct pstloc_get_module_status_resp)) )
    {
        utils_cond_signal(&nwking_state, dup);
    }
}



static unsigned char chksum(const void *data, unsigned int len)
{
    unsigned char sum = 0;
    const unsigned char *d = (const unsigned char *)data;

    if (!data)
        return 0;

    for (unsigned int i = 0; i < len; ++i)
        sum += d[i];

    return sum;
}


int zm32_spec_tx_frame(const void *dst, unsigned int dst_sz, const void *data, unsigned int len)
{
    teSL_Status status;
    unsigned char buf[MAX_UNICAST_SZ + 14];
    unsigned int i = 0;
    unsigned short panid = 0xFFFE;
    unsigned long long int mac;

    if (!dst)
        return -1;
    if (dst_sz != 2 && dst_sz != 8)
        return -2;
    if (len > 0 && !data)
        return -3;
    if (len > MAX_UNICAST_SZ)
        return -4;

    if (dst_sz == 8) {
        memcpy(&mac, dst, sizeof(mac));
        mac = htonll(mac);
        memcpy(buf+i, &mac, sizeof(mac));
        i += 8;
        buf[i++] = panid >> 8U;
        buf[i++] = panid;
    } else {
        memset(buf+i, 0, 8);
        i += 8;
        memcpy(&panid, dst, sizeof(panid));
        buf[i++] = panid >> 8U;
        buf[i++] = panid;
        if (0xFFFF == panid && len > MAX_BROADCAST_SZ)
            return -5;
        if (0xFFFB == panid && len > MAX_MULTCAST_SZ)
            return -6;
    }
    buf[i++] = len >> 8U;
    buf[i++] = len;
    if (len) {
        memcpy(buf+i, data, len);
        i += len;
    }
    buf[i] = chksum(buf, i);
    buf[i] += E_SL_SPC_TX_FRAME;
    buf[i] += E_SL_SPC_TX_FRAME >> 8;
    buf[i] += E_SL_SPC_TX_FRAME >> 16;
    buf[i] += E_SL_SPC_TX_FRAME >> 24;
    ++i;

    status = eSL_SendMessage(E_SL_SPC_TX_FRAME, i, buf);
    if ( E_SL_OK != status )
        return -7;

    return 0;
}


int zm32_spec_grptx_frame(unsigned short grp, const void *data, unsigned int len)
{
    teSL_Status status;
    unsigned char buf[MAX_MULTCAST_SZ + 14];
    unsigned int i = 0;

    if (len > 0 && !data)
        return -1;
    if (len > MAX_MULTCAST_SZ)
        return -2;

    buf[i++] = grp >> 8U;
    buf[i++] = grp;
    buf[i++] = len >> 8U;
    buf[i++] = len;
    if (len) {
        memcpy(buf+i, data, len);
        i += len;
    }
    buf[i] = chksum(buf, i);
    buf[i] += E_SL_SPC_GROUP_TX_FRAME;
    buf[i] += E_SL_SPC_GROUP_TX_FRAME >> 8;
    buf[i] += E_SL_SPC_GROUP_TX_FRAME >> 16;
    buf[i] += E_SL_SPC_GROUP_TX_FRAME >> 24;
    ++i;

    status = eSL_SendMessage(E_SL_SPC_GROUP_TX_FRAME, i, buf);
    if ( E_SL_OK != status )
        return -3;

    return 0;
}

extern void zm32_msg_proc(void *msg);
void zm32_spec_rx_frame(void *msg)
{
    struct frame_rtx *recv = (struct frame_rtx *)msg;

    recv->macaddr = ntohll(recv->macaddr);
    recv->nwkaddr = ntohs(recv->nwkaddr);
    recv->length  = ntohs(recv->length);
    printf("spec recv mac:%016llX, nwk:%04X\n", recv->macaddr, recv->nwkaddr);
    printf("payload:\n");
    for (int i = 0; i < recv->length; ++i)
        printf("%02X ", recv->payload[i]);
    printf("\n");
    zm32_msg_proc(msg);
}


void zm32_spec_grprx_frame(void *msg)
{
    struct group_frame_rx *recv = (struct group_frame_rx *)msg;

    recv->groupid    = ntohs(recv->groupid);
    recv->srcmacaddr = ntohll(recv->srcmacaddr);
    recv->srcnwkaddr = ntohs(recv->srcnwkaddr);
    recv->length     = ntohs(recv->length);
    printf("spec grprx mac:%016llX, nwk:%04X\n", recv->srcmacaddr, recv->srcnwkaddr);
    printf("payload:\n");
    for (int i = 0; i < recv->length; ++i)
        printf("%02X ", recv->payload[i]);
    printf("\n");
}



int zm32_tmploc_tx_mode_req(unsigned  char mode, unsigned char needmac,
                            unsigned char addrmode, unsigned char format)
{
    teSL_Status status;
    struct tmploc_set_tx_mode_req req;

    memset(&req, 0x0, sizeof(req));
    req.txmode      = mode;
    req.needmac     = needmac;
    req.addressmode = addrmode;
    req.format      = format;

    status = eSL_SendMessage(E_SL_TMP_LOC_TX_MODE, sizeof(struct tmploc_set_tx_mode_req), &req);
    if ( E_SL_OK != status )
        return -1;

    return 0;
}


void zm32_tmploc_tx_mode_resp(void *msg)
{
    struct tmploc_set_tx_mode_resp *dup;
    struct tmploc_set_tx_mode_resp *resp = (struct tmploc_set_tx_mode_resp *)msg;

    if ( EN_UTILS_OK == utils_mem_dup(( void ** ) &dup, resp, sizeof(struct tmploc_set_tx_mode_resp)) )
    {
        utils_cond_signal(&tx_cnf, dup);
    }
}


int zm32_tmpmac_rx_mode_req(unsigned long long macaddr, unsigned char mode)
{
    teSL_Status status;
    struct tmpmac_set_rx_mode_req req;
    
    memset(&req, 0x0, sizeof(req));
    req.mode    = mode;
    req.macaddr = htonll(macaddr);

    status = eSL_SendMessage(E_SL_TMP_MAC_RX_MODE, sizeof(struct tmpmac_set_rx_mode_req), &req);
    if ( E_SL_OK != status )
        return -1;

    return 0;
}


void zm32_tmpmac_rx_mode_resp(void *msg)
{
    struct tmpmac_set_rx_mode_resp *dup;
    struct tmpmac_set_rx_mode_resp *resp = (struct tmpmac_set_rx_mode_resp *)msg;

    resp->macaddr = ntohll(resp->macaddr);
    if ( EN_UTILS_OK == utils_mem_dup(( void ** ) &dup, resp, sizeof(struct tmpmac_set_rx_mode_resp)) )
    {
        utils_cond_signal(&rx_cnf, dup);
    }
}


int zm32_permit_join_req(unsigned short dur)
{
    teSL_Status status;
    struct pstloc_permit_join_req req;

    req.duration = htons(dur);

    status = eSL_SendMessage(E_SL_PST_LOC_PERMIT_JOIN, sizeof(struct pstloc_permit_join_req), &req);
    if ( E_SL_OK != status )
        return -1;

    return 0;
}


void zm32_permit_join_resp(void *msg)
{
    struct pstloc_permit_join_resp *dup;
    struct pstloc_permit_join_resp *resp = (struct pstloc_permit_join_resp *)msg;

    resp->duration = ntohs(resp->duration);

    if ( EN_UTILS_OK == utils_mem_dup(( void ** ) &dup, resp, sizeof(struct pstloc_permit_join_resp)) )
    {
        utils_cond_signal(&join, dup);
    }
}